const fs = require('fs');
const _ = require('lodash');
const path = require('path');
const rimraf = require('rimraf');

var AdmZip = require('adm-zip');

const fileExt = ['tar', 'zip'];
const METAFILE = 'unzip_meta.json';

module.exports = {
  // APP_ROOT: path.join(path.dirname(process.mainModule.filename), '../'),
  APP_ROOT: process.cwd(),
  readdir(path) {
    return new Promise((resolve, reject) => {
      fs.readdir(path, (err, files) => {
        if(err) reject(err);
        resolve(files);
      })
    });
  },
  /**
   * 通过后缀名判断该文件是否可被解压
   * @param {string} filename 
   */
  canExtract(filename) {
    filename = filename.trim();
    let isExtractFile = false;
    for (var i = 0; i < fileExt.length; i++) {
      if (filename.lastIndexOf(fileExt[i]) === filename.length - fileExt[i].length) {
        isExtractFile = true;
        break;
      }
    }
    return isExtractFile;
  },
  /**
   * 
   * @param {string} filepath
   * @return { Promise<{ name: string, ctime: string, size: string }> }
   */
  getFileDetail(filepath) {
    return new Promise((resolve, reject) => {
      const fileInfo = {};
      fs.stat(filepath, (err, stats) => {
        if (err) {
          reject(err);
        } else {
          resolve({
            name: filepath.split(/[\\\/]/).pop(),
            ctime: this.trimDate(stats.ctime),
            size: this.trimFileSize(stats.size)
          });
        }
      });
    });
  },
  /**
   * new Date() -> 2017-5-18 13:39:07
   * @param {Date} date 
   */
  trimDate(date) {
    return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours() < 10 ? '0' + date.getHours() : date.getHours()}:${date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()}:${date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()}`
  },
  /**
   * 
   * @param {number} bytes 
   * @return { string }
   */
  trimFileSize(bytes) {
    if (bytes < 1024) {
      return bytes + 'B';
    } else if (bytes < 1024 * 1024) {
      return (bytes / (1024)).toFixed(2) + 'KB';
    } else {
      return (bytes / (1024 * 1024)).toFixed(2) + 'MB';
    }
  },
  /**
   * 延迟执行
   * @param {Number} ms 
   * @return {{ Promise }}
   */
  delay(ms) {
    return new Promise(resolve => {
      setTimeout(resolve, ms);
    });
  },
  /**
   * 列出所有目录下的meta信息 
   * @param {String} DIR_ROOT 
   * @return {Array<{ from: string, dirName: string, dirPath: string }>}
   * 
   */
  listFileMetaData(subPath) {
    const DIR_ROOT = path.join(this.APP_ROOT, subPath);
    return this.readdir(DIR_ROOT)
      .then(fileNames => _.chain(fileNames)
        .map(fileName => {
          // 组合文件路径
          return {
            dirName: fileName,
            dirPath: path.join(DIR_ROOT, fileName)
          }
        })
        // 筛选合法目录
        .map(({ dirPath, dirName }) => {
          // 筛选目录
          const metaFilePath = path.join(dirPath, METAFILE);
          let metaData = null;
          
          // 拥有元数据文件
          try {
            metaData = fs.readFileSync(metaFilePath, 'utf8');
            metaData = JSON.parse(metaData);
            metaData.dirName = dirName;
            metaData.dirPath = dirPath;
            
          } catch(err) {
            metaData = null;
          } finally {
            return metaData;
          }
        })
      .compact()
      .value());
  },
  /**
   * 文件是否可访问（存在）
   * @param {string} path 
   * @param {string} flag 'r'
   * @return { Promise<any> }
   */
  isFileAccessable(path, flag = 'r') {
    return new Promise((resolve, reject) => {
      fs.access(path, flag, (err, fd) => {
        if (err) {
          return reject(err);
        } else {
          return resolve(fd);
        }
      })
    });
  },
  /**
   * 文件/目录是否存在
   * @param {string} path 
   */
  isFileAccessableSync(path) {
    try {
      fs.accessSync(path);
      return true;
    } catch (err) {
      return false;
    }
  },
  /**
   * 
   * @param {string} zipPath zip文件地址
   * @param {string} target 目标文件夹
   * @return { Promise<any> }
   */
  extractFile(zipPath, target = path.join(process.cwd(), 'output')) {
    return new Promise(resolve => {
      try {
        var zip = new AdmZip(zipPath);
        var zipEntries = zip.getEntries(); // an array of ZipEntry records
        zip.extractAllTo(target, /*overwrite*/true);
        resolve();
      } catch (err) {
        reject(new Error('extra folder error'));
      }
    })
  },

  /**
   * 通过文件名删除文件夹和文件
   * @param {string} fileId 文件名
   * @param { string } DIR_ROOT 根目录
   * @return {Promise<any>}
   */
  deleteFilesById(fileId) {
    const DIR_WWW = path.join(this.APP_ROOT, 'www', fileId);
    const DIR_FILES = path.join(this.APP_ROOT, 'files');

    let deleteTask = [];
    deleteTask.push(new Promise((resolve, reject) => {
      fs.unlink(path.join(DIR_FILES, fileId), err => {
        if (err) {
          return reject(err);
        }
        return resolve();
      });
    }));
    deleteTask.push(new Promise((resolve, reject) => {
      try {
        rimraf.sync(DIR_WWW);
        resolve();
      } catch(err) {
        reject(err);
      }
    }));
    
    return Promise.all(deleteTask);
  },
  deleteFilesById1(fileId) {
    const DIR_WWW = path.join(this.APP_ROOT, 'www');
    const DIR_FILES = path.join(this.APP_ROOT, 'files');
    this.listFileMetaData('www').then(metaDataList => {
      const tarDirMeta = metaDataList.filter(metaDate => fileId === metaDate.from)[0] || null;
      let deleteTask = [];
      deleteTask.push(new Promise((resolve, reject) => {
        fs.unlink(path.join(DIR_FILES, fileId), err => {
          if (err) {
            return reject(err);
          }
          return resolve();
        });
      }));
      if (tarDirMeta) {
        deleteTask.push(new Promise((resolve, reject) => {
          try {
            rimraf.sync(tarDirMeta.dirPath);
            resolve();
          } catch(err) {
            reject(err);
          }
        }));
      }
      return Promise.all(deleteTask);
    });
  },
  /**
   * 如果路径不存在，则创建文件夹（相对于项目路径下）
   * @param {string} path 文件夹路径
   * @return {Promise}
   */
  createFolder(dirPath) {
    return new Promise((resolve, reject) => {
      try {
        let path = dirPath.replace(/^[\/\\]+/, '');
        _.reduce(path.split(/[\/\\]+/g), (currentPath, value, key) => {
          currentPath += '/' + value;
          try {
            fs.accessSync(currentPath);
          } catch(fileNotAccess) {
            fs.mkdirSync(currentPath);
          }
          return currentPath;
        }, process.cwd());
        resolve(dirPath);
      } catch(err) {
        console.error(err);
        reject(err);
      }
    });
  }
}